Amazon CloudWatch Alarmのアラーム発生時にSNS Topic経由で起動したLambda内で直近のデータポイントの値を取得する
こんにちは、CX事業本部 IoT事業部の若槻です。
Amazon CloudWatch Alarmのアラーム発生時にSNS Topic経由で起動したLambda内で、アラームが発生したメトリクスの直近のデータポイント値を取得したいことがありました。
アラーム発生時に発行されるイベントメッセージには様々な情報が含まれるため、
- そもそもイベントメッセージ内に直近のデータポイント値は含まれているのか?
- 含まれている場合のデータポイント値の取り出し方は?
という観点で今回は検証を行ってみました。
やってみた
実装
AWS CDKで下記構成の実装を行います。
下記はCDK Stackのコードです。
import { Construct } from 'constructs'; import { aws_lambda_nodejs, aws_sns, aws_cloudwatch, aws_cloudwatch_actions, Duration, Stack, StackProps, aws_lambda_event_sources, } from 'aws-cdk-lib'; export class ProcessStack extends Stack { constructor(scope: Construct, id: string, props: StackProps) { super(scope, id, props); //CloudWatch Alarm const sampleAlarm = new aws_cloudwatch.Alarm(this, 'sampleAlarm', { alarmName: 'sampleAlarm', metric: new aws_cloudwatch.Metric({ namespace: 'StateMachinePublish', metricName: 'temperature', statistic: aws_cloudwatch.Statistic.MAXIMUM, period: Duration.minutes(1), }), evaluationPeriods: 1, threshold: 5, comparisonOperator: aws_cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD, }); //SNS Topic const cloudwatchAlarmNotifyTopic = new aws_sns.Topic( this, 'cloudwatchAlarmNotifyTopic' ); //Alarmアクション追加 sampleAlarm.addAlarmAction( new aws_cloudwatch_actions.SnsAction(cloudwatchAlarmNotifyTopic) ); //Lambda関数 const sampleFunc = new aws_lambda_nodejs.NodejsFunction( this, 'sampleFunc', { functionName: 'sampleFunc', entry: 'src/lambda/handler.ts', } ); //Lambdaイベントソース追加 sampleFunc.addEventSource( new aws_lambda_event_sources.SnsEventSource(cloudwatchAlarmNotifyTopic) ); } }
下記はLambdaコードです。この中でアラーム発生時に発行されるイベントメッセージをパースし、コンソール出力しています。
interface Event { Records: { Sns: { Message: string } }[]; } export const handler = (event: Event): void => { const message = JSON.parse(event.Records[0].Sns.Message); console.log(message); };
CDK Deployして実装をデプロイします。
アラームを発生させてデータポイントが含まれたメッセージを取得してみる
CloudWatch metricのデータポイントの値として1
をPutしてアラームを発生させます。
[ { "MetricName": "temperature", "Value": 1, "Unit": "Count" } ]
$ NAME_SPACE=sampleNameSpace $ aws cloudwatch put-metric-data \ --namespace $NAME_SPACE \ --metric-data file://metric.json $ ALARM_NAME=sampleAlarm $ aws cloudwatch set-alarm-state \ --alarm-name $alarmName \ --state-value "ALARM" \ --state-reason "test"
アラームが発生しました。
するとLambdaが起動し、CloudWatch Logsには次のログが出力されていました。このうちNewStateReason
フィールドに直近のデータポイントの情報(1 datapoint [1.0 (04/06/22 15:04:00)]
)が入っているのが確認できます。
{ AlarmName: 'sampleAlarm', AlarmDescription: null, AWSAccountId: 'xxxxxxxxxxxx', AlarmConfigurationUpdatedTimestamp: '2022-06-04T15:01:50.499+0000', NewStateValue: 'ALARM', NewStateReason: 'Threshold Crossed: 1 datapoint [1.0 (04/06/22 15:04:00)] was less than or equal to the threshold (5.0).', StateChangeTime: '2022-06-04T15:05:18.979+0000', Region: 'Asia Pacific (Tokyo)', AlarmArn: 'arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:sampleAlarm', OldStateValue: 'INSUFFICIENT_DATA', OKActions: [], AlarmActions: [ 'arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:ProcessStack-cloudwatchAlarmNotifyTopic85FD23D4-VAGGJ15VXMNK' ], InsufficientDataActions: [], Trigger: { MetricName: 'temperature', Namespace: 'sampleNameSpace', StatisticType: 'Statistic', Statistic: 'MAXIMUM', Unit: null, Dimensions: [], Period: 60, EvaluationPeriods: 1, ComparisonOperator: 'LessThanOrEqualToThreshold', Threshold: 5, TreatMissingData: '', EvaluateLowSampleCountPercentile: '' } }
上記は単一のデータポイントが評価対象の場合です。では評価期間を変更して、複数のデータポイントを評価対象とした場合はどうなるでしょうか。
CloudWatch Alarmの評価期間を1
から3
に変更します。CDK Deployして変更を反映します。
//CloudWatch Alarm const sampleAlarm = new aws_cloudwatch.Alarm(this, 'sampleAlarm', { alarmName: 'sampleAlarm', metric: new aws_cloudwatch.Metric({ namespace: 'sampleNameSpace', metricName: 'temperature', statistic: aws_cloudwatch.Statistic.MAXIMUM, period: Duration.minutes(1), }), evaluationPeriods: 3, threshold: 5, comparisonOperator: aws_cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD, });
複数のデータポイントをPutし、アラームを発生させます。
すると次は2 datapoints [2.0 (04/06/22 15:41:00), 4.0 (04/06/22 15:39:00)]
と2つのデータポイントの値が取得できています。前方のデータポイントが直近のもののようですね。
{ AlarmName: 'sampleAlarm', AlarmDescription: null, AWSAccountId: 'xxxxxxxxxxxx', AlarmConfigurationUpdatedTimestamp: '2022-06-04T15:33:59.042+0000', NewStateValue: 'ALARM', NewStateReason: 'Threshold Crossed: 2 datapoints [2.0 (04/06/22 15:41:00), 4.0 (04/06/22 15:39:00)] were less than or equal to the threshold (5.0).', StateChangeTime: '2022-06-04T15:42:08.298+0000', Region: 'Asia Pacific (Tokyo)', AlarmArn: 'arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:sampleAlarm', OldStateValue: 'INSUFFICIENT_DATA', OKActions: [], AlarmActions: [ 'arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:ProcessStack-cloudwatchAlarmNotifyTopic85FD23D4-VAGGJ15VXMNK' ], InsufficientDataActions: [], Trigger: { MetricName: 'temperature', Namespace: 'sampleNameSpace', StatisticType: 'Statistic', Statistic: 'MAXIMUM', Unit: null, Dimensions: [], Period: 60, EvaluationPeriods: 3, ComparisonOperator: 'LessThanOrEqualToThreshold', Threshold: 5, TreatMissingData: '', EvaluateLowSampleCountPercentile: '' } }
NewStateReasonからデータポイント値を取り出す
newStateReasonはString型のため文字列処理をして直近のデータポイント値を取り出す必要があります。ちょっと無理やりですが次のようにすれば、含まれているデータポイントが1つまたは複数のいずれの場合でも直近のものを取り出せました。
> newStateReason="Threshold Crossed: 1 datapoint [1.0 (04/06/22 15:04:00)] was less than or equal to the threshold (5.0)." > Number( newStateReason .split(']')[0] .split('[')[1] .split(',')[0] .split('(')[0] .trim() ); 1 > newStateReason="Threshold Crossed: 2 datapoints [2.0 (04/06/22 15:41:00), 4.0 (04/06/22 15:39:00)] were less than or equal to the threshold (5.0)." > Number( newStateReason .split(']')[0] .split('[')[1] .split(',')[0] .split('(')[0] .trim() ); 2
参考
- put-metric-data — AWS CLI 1.25.2 Command Reference
- put-metric-data — AWS CLI 1.25.2 Command Reference
以上